package cn.echo.consul.manager.response;

import cn.echo.consul.manager.exception.BizServiceException;
import cn.echo.consul.manager.exception.ForbiddenException;
import cn.echo.consul.manager.exception.InnerServerException;
import cn.echo.consul.manager.exception.UnauthorizedException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ServerWebInputException;

import java.io.IOException;
import java.net.SocketException;
import java.util.List;

import static cn.echo.consul.manager.response.ResultCode.*;

@Slf4j
@ResponseBody
@ControllerAdvice
public class DefaultGlobalExceptionHandler {

    @InitBinder
    public void globalInitBinder(WebDataBinder binder) {
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"));
    }


    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public ResultError methodArgumentNotValidException(MethodArgumentNotValidException ex) {
        log.warn("method argument not valid exception:{}", ex.getMessage());
        ResultError result = null;
        List<ObjectError> objectErrorList = ex.getBindingResult().getAllErrors();
        if (objectErrorList.size() > 0) {
            result = new ResultError(F11201, objectErrorList.get(0).getDefaultMessage());
        } else {
            result = new ResultError(F11201, ex.getMessage());
        }
        return result;
    }

    @ResponseBody
    @ExceptionHandler(value = {IllegalArgumentException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResultError illegalArgumentException(IllegalArgumentException ex) {
        log.warn("Illegal argument exception:{}", ex.getMessage());
        return new ResultError(F11201, ex.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = {BizServiceException.class})
    public ResultError bizServiceException(BizServiceException ex) {
        log.warn("biz service exception:{}, {}", ex.getCode(), ex.getMsg());
        return new ResultError(ex.getCode(), ex.getMsg());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = {InnerServerException.class})
    public ResultError innerServiceException(InnerServerException ex) {
        log.warn("inner service exception:{}, {}", ex.getCode(), ex.getMsg());
        return new ResultError(ex.getCode(), ex.getMsg());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(value = {UnauthorizedException.class})
    public ResultError unauthorizedException(UnauthorizedException ex) {
        log.warn("unauthorized exception:{}, {}", ex.getCode(), ex.getMsg());
        return new ResultError(ex.getCode(), ex.getMsg());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(value = {ForbiddenException.class})
    public ResultError forbiddenException(ForbiddenException ex) {
        log.warn("forbidden exception:{}, {}", ex.getCode(), ex.getMsg());
        return new ResultError(ex.getCode(), ex.getMsg());
    }


    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = {ServerWebInputException.class})
    public ResultError serverWebInputException(ServerWebInputException ex) {
        log.warn("not present input exception", ex);
        return new ResultError(F11201, ex.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = {HttpMessageNotReadableException.class})
    public ResultError httpMessageNotReadableException(HttpMessageNotReadableException ex) {
        log.warn("not readable exception", ex);
        return new ResultError(F11201, ex.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = {DuplicateKeyException.class})
    public ResultError duplicateKeyException(DuplicateKeyException ex) {
        log.warn("SQL duplicate key exception", ex);
        String msg = ex.getMessage();
        if (ex.getMessage() != null) {
            msg = ex.getMessage().substring(ex.getMessage().lastIndexOf("Duplicate"));
        }
        return new ResultError(F11202, msg);
    }

    @ResponseBody
    @ExceptionHandler(value = {RuntimeException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultError runtimeException(RuntimeException ex) {
        log.error("runtime exception,message:{},stack:{}", ex.getMessage(), ex.getStackTrace());
        return new ResultError(F11405, ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {NullPointerException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultError nullPointerException(NullPointerException ex) {
        log.error("null pointer exception,message:{},stack:{}", ex.getMessage(), ex.getStackTrace());
        return new ResultError(F11406, ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {IOException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultError ioException(IOException ex) {
        log.error("io exception,message:{},stack:{}", ex.getMessage(), ex.getStackTrace());
        return new ResultError(F11407, ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {ArrayIndexOutOfBoundsException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultError arrayIndexOutOfBoundsException(ArrayIndexOutOfBoundsException ex) {
        log.error("array index out of bounds exception,message:{},stack:{}", ex.getMessage(), ex.getStackTrace());
        return new ResultError(F11408, ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {SocketException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultError socketException(SocketException ex) {
        log.error("socket exception,message:{},stack:{}", ex.getMessage(), ex.getStackTrace());
        return new ResultError(F11409, ex.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = {UnsupportedOperationException.class})
    public ResultError unsupportedOperationException(UnsupportedOperationException ex) {
        log.error("unsupported operation exception,message:{},stack:{}", ex.getMessage(), ex.getStackTrace());
        return new ResultError(F11400, ex.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = {Exception.class})
    public ResultError exception(Exception ex) {
        log.error("internal exception,message:{},stack:{}", ex.getMessage(), ex.getStackTrace());
        return new ResultError(F11400, ex.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = {Throwable.class})
    public ResultError throwable(Throwable ex) {
        log.error("internal throwable,message:{},stack:{}", ex.getMessage(), ex.getStackTrace());
        return new ResultError(F11400, ex.getMessage());
    }
}